# -*- coding: utf-8 -*-
from __future__ import absolute_import

import os
import sys

# add up one level dir into sys path
sys.path.append(os.path.abspath(os.path.dirname(
    os.path.dirname(os.path.dirname(__file__)))))
os.environ['DJANGO_SETTINGS_MODULE'] = 'base.settings'

import time
import json

from common.cache import redis_cache
from common import orm

from common.utils.tz import now_ts
from common.utils import EnhencedEncoder
from common.utils import thread
from common.timer import TIMER_EVENT_TYPE, handler

from django.conf import settings
import logging.config
from common.utils import track_logging

logging.config.dictConfig(settings.LOGGING)

_LOGGER = track_logging.getLogger(__name__)

DEFAULT_EVENT_HANDLERS = {
    TIMER_EVENT_TYPE.MCH_NOTIFY: handler.MchNotifyHandler(),
    TIMER_EVENT_TYPE.WITHDRAW_NOTIFY: handler.WithdrawNotifyHandler(),
}


class EventProcessor(thread.Thread):
    """
    timer event processor
    """

    def __init__(self, event_type):
        thread.Thread.__init__(self)
        self.event_type = event_type
        self.event_handler = DEFAULT_EVENT_HANDLERS.get(event_type)
        self.sleep_second = 0.5
        if not self.event_handler:
            _LOGGER.error('event handler not found for %s', TIMER_EVENT_TYPE.get_label(event_type))
            return

    def get_expired_events(self):
        event_list = []
        max_time = now_ts()
        value_list = redis_cache.range_expired_events(self.event_type, max_time)
        for event_value in value_list:
            event_list.append(event_value)

        return event_list

    def run(self):
        _LOGGER.info("start event processor, event: [%s]" % (self.event_type,))
        while not self.shutdown_flag.is_set():
            events = self.get_expired_events()
            if not events:
                time.sleep(self.sleep_second)

            for event_value in events:
                try:
                    self._process(event_value)
                except:
                    _LOGGER.exception('event value invalid.(%s:%s)' % (self.event_type, event_value))
                finally:
                    orm.session.close()

    def _process(self, event_value):
        event_msg = json.loads(event_value)
        success = self.event_handler.process(event_msg['msg'])
        # remove timer key
        redis_cache.remove_expired_event(self.event_type, event_value)
        if not success:
            try_count = event_msg.get('try_count', 0)
            max_try = self.event_handler.MAX_TRY
            if max_try == 0 or (  # 无限制
                    max_try and try_count < max_try):
                try_count += 1
                # 以2的指数次方退避，最多为1分钟
                interval = min(2 ** try_count, 60)
                next_exec_time = now_ts() + interval
                event_msg['try_count'] = try_count
                msg = json.dumps(event_msg, ensure_ascii=False,
                                 cls=EnhencedEncoder)
                redis_cache.submit_timer_event(self.event_type, msg, next_exec_time)


if __name__ == "__main__":
    thread.GracefulExitedExecutor('Timer Processor', [EventProcessor(e) for e in TIMER_EVENT_TYPE.to_dict()]).start()
